package br.ufg.inf.sd.truco;

import java.net.MalformedURLException;
import java.rmi.Naming;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;
import java.rmi.RMISecurityManager;
import java.rmi.server.UnicastRemoteObject;

/**
 * Esta classe implementa a interface TrucoInterface e herda da classe UnicastRemoteObject. Sua 
 * função eh controlar toda a relação entre os clientes do jogo (isto inclui cuidar para que as regras sejam 
 * cumpridas e o placar atualizado corretamente
 * @author Egio Arruda Junior & André Vilas Boas
 * @version 1.0
 */

public class TrucoServer extends UnicastRemoteObject implements TrucoInterface {
	
	private static final long serialVersionUID = 1L;		//nao se preocupe com este campo matuto, ele eh apenas uma necessidade para o eclipse (ignore essa linha
	private int cont;										//responsável por controlar o numero de jogadores na atual partida
	private JogadorInterface[] players;						//vetor de objetos do tipo jogador
	private Baralho baralho;								//vetor que representa o baralho
	private int valorRodada = 1;							//valor atual da rodada
	/*
	 * esta variavel eh responsavel por inidicar se a partida esta trucada ou nao.
	 * Caso o jogo esteja trucado esta variavel ira indicar true, caso contrario false.
	 */
	private boolean truco;		
	
	/*
	 * Esta variavel irá indicar o numero da rodada atual.
	 */
	private int rodadaAtual;
	
	/*
	 * Esta variavel de controle é responsável por nos indicar qual jogador esta com vez de jogar.
	 */	
	private int jogador_atual;
	
	/*
	 * Esta variavel de controle fornece a quantidade de jogadores que já jogaram no round.
	 */
	private int cont_jogador;
	
	/*
	 * Este campo indica a pontuacao de uma rodada
	 * neste vetor o valor da primeira posicao no vetor indica  a dupla 1 e a segunda a equipe 2.
	 * O valor maximo deste campo deverá ser 12. 
	 */
	private int[][] placarRodadas;
	

	/*
	 * Este campo indica a pontuacao de uma determinada partida,
	 * neste vetor o valor da primeira posicao no vetor indica  a dupla 1 e a segunda a equipe 2..
	 * O valor maximo deste campo deverá ser 12. 
	 */
	private int[] placarPartida;
	
	/*
	 * Este campo indica a pontuacao da quedas,
	 * neste vetor o valor da primeira posicao no vetor indica  a dupla 1 e a segunda a equipe 2..
	 * Não existe limite para este campo 
	 */
	private int[] placarQuedas;
	
	/*
	 * Este campo armazena as cartas jogadas na queda.
	 */
	private Carta[] cartas_rodada;
	
	private int cont_rodada;
	
	public TrucoServer() throws RemoteException {
		super();
		//inicializando todas as variaveis de controle do jogo
                cont = 0;
		truco = false;
		rodadaAtual = 0;
		jogador_atual = 0;
		cont_jogador = 0;
		cont_rodada = 0;
		placarRodadas =  new int[3][2];
		for(int i=0; i<3; i++)
			for(int j=0; j<2; j++)
				placarRodadas[i][j] = 0;

		placarPartida =  new int[2];
		placarPartida[0] = 0;
		placarPartida[1] = 0;
		placarQuedas =  new int[2];
		placarQuedas[0] = 0;
		placarQuedas[1] = 0;
		players = new JogadorInterface[4];
		players[0] = null;
		players[1] = null;
		players[2] = null;
		players[3] = null;
		baralho = new Baralho();	//o baralho é construído aqui
		cartas_rodada = new Carta[4];
		inicializarCartasRodada(); //as cartas da rodada sao inicializadas;
		
	}

	public int join(String identyfier, String host) throws RemoteException{
		int status = 0;
		if(cont < 4){
			
			if( System.getSecurityManager() == null )
				System.setSecurityManager(new RMISecurityManager());

			String caminho = new String("//" + host + "/TrucoClientes");

			try {
				this.players[cont] = (JogadorInterface)Naming.lookup(caminho);
			}catch (MalformedURLException e) {
				status = 1;
				e.printStackTrace();
			}catch (RemoteException e) {
				status = 1;
				e.printStackTrace();
			}catch (NotBoundException e) {
				status = 1;
				e.printStackTrace();
			}

			System.out.println("Jogador " + (cont+1) + " esta conectado!");

			if(cont==3){
				cont=4;
				start(); //Caso o quarto jogador tenha sido criado, o jogo deve iniciar;			
			}

			cont++;

		}else{
			System.out.println("Servidor esta cheio!");
		}
		return status;
	}


	public void play(Carta carta) throws RemoteException{
		
		//Adicionar a carta ao vetor de cartas da rodada, e ceder a vez para o proximo jogador
		if(cont_jogador < 4){
			cartas_rodada[jogador_atual] = carta;

	        players[jogador_atual].travar();
	        
		System.out.println("Jogador " + jogador_atual + " jogou!");

	        // exibe aos jogadores a carta selecionada
	        /*for(int i=0; i<4; i++)
	        	players[i].setCartaJogada(jogador_atual, carta);*/

		System.out.println("Clientes sabem qual carta foi jogada!");
	        
	        jogador_atual++;
	        //liberar o proximo jogador
	        players[jogador_atual].destravar();
		
		System.out.println("Proximo jogador destravado");
	        
	        if(cont_jogador == 3){
	        	processaRodada(cartas_rodada);
	        }
	        
		System.out.println("Rodada processada");

	        cont_jogador++;
		}
	}
	
	public void truco() throws RemoteException{
		if(truco == true && valorRodada == 1)
			valorRodada = 3;
		if(truco == true && valorRodada == 3)
			valorRodada = 6;
		if(truco == true && valorRodada == 6)
			valorRodada = 9;
		if(truco == true && valorRodada == 9)
			valorRodada = 12;
		truco = true;
	}

	public boolean isTrucado() throws RemoteException{
		return truco;
	}

	public void correr() throws RemoteException{

		//TODO Jogador desiste dos pontos da rodada, seja com ou sem truco;

	}

	public void sairDoJogo(int id) throws RemoteException{
		System.out.println("O jogo deverá ser encerrado devido a desistência do jogador " + id);
		players[0].quit(id);
		players[1].quit(id);
		players[2].quit(id);
		players[3].quit(id);
		System.out.println("Fechando o servidor...");
		System.exit(0);
	}

	//inicializa o vetor de Cartas da rodada;
	public void inicializarCartasRodada(){
		for(int i = 0; i < 4; i++){
			cartas_rodada[i] = new Carta();
			cartas_rodada[i].setNome("");
			cartas_rodada[i].setNipe("");
			cartas_rodada[i].setValor(0);
			cartas_rodada[i].setSelecionada(false);
		}
	}
	
	
	/** Inicializa a rodada e distribui as cartas
	 *  Sequencia de execuçao:
	 *  1) start() - libera um jogador para inicializar a partida.
	 *  2) distribuirCartas() - cada jogador recebe suas cartas
	 *  3) Aguarda o primeiro jogador a executar o play() - A ação e passada para o proximo jogador
	 */
	public void start() throws RemoteException{



		//Realizar lock nos jogadores
		for(int i = 0; i < 4; i++){
			players[i].travar();
		}

        System.out.println("Jogadores travados!");
		
		//Seta o contador de jogador para 0
		cont_jogador = 0;
		
                
		//libera o jogador inicial
		players[rodadaAtual%4].destravar(); //A cada rodada o proximo jogador é pé da partida
        System.out.println("Jogador inicial liberado!");
		System.out.println("Distribuindo cartas...");
		distribuirCartas();
	}
	
	//Funcao que realiza a distribuicao das cartas aos jogadores
	public void distribuirCartas() throws RemoteException{
		
		for(int i = 0; i < 4; i++){
			this.players[i].setCartas(this.baralho.distribuirCartas());
		}		
        System.out.println("Cartas distribuidas...");
	}
	
	public void processaRodada(Carta[] jogada_rodada) throws RemoteException{
		
		int aux = 0; //Variavel auxiliar que armazena o valor da maior carta da partida
		int indice = 0; //Variavel auxiliar que armazena o indice da maior carta identificando o jogador que a jogou
		int cont = 0; //Variavel criada para controle do laço de repetiçao
		int dupla = 0; //Variavel que armazena a dupla vitoriosa da partida
		int parcial_dupla1 = 0;
		int parcial_dupla2 = 0;
		boolean cangado = false;
		
		aux = jogada_rodada[cont].getValor();
		
		//percorrer o vetor de cartas da jogada para detectar a maior carta
		while(cont < 3){
			if(aux < jogada_rodada[cont].getValor()){
				aux = jogada_rodada[cont].getValor();
				indice = cont;
			}
			cont++;				
		}
		
		//Percorre o vetor de catas afim de verificar se a maior carta no jogo esta repetida (cangado)
		for(int i=0; i < 3; i++){
			if(jogada_rodada[indice].getValor() == jogada_rodada[i].getValor())
				if(indice != i)
					cangado = true;
		}
		
		if(cangado){
			this.placarRodadas[cont_rodada][0]=1;
			this.placarRodadas[cont_rodada][1]=1;
		}else{
			if(indice == 0 || indice == 2){
				dupla = 0;
			}else
				dupla = 1;
			this.placarRodadas[cont_rodada][dupla]=1;
		}
		
		//Caso já esteje na segunda rodada, verifica se já existe ganhador
		
		if(cont_rodada >= 1){
			for(int i=0; i < 3; i++){
				parcial_dupla1 += placarRodadas[i][0];
				parcial_dupla2 += placarRodadas[i][0];
			}
			if(parcial_dupla1 > parcial_dupla2){
				if(truco){
					this.placarPartida[0] = this.placarPartida[0] + 3;
				}else{
					this.placarPartida[0] = this.placarPartida[0] + 1;
				}
			}else{
				if(truco){
					this.placarPartida[1] = this.placarPartida[1] + 3;
				}else{
					this.placarPartida[1] = this.placarPartida[1] + 1;
				}
			}
			
			//Atualiza os pontos em todos os clientes conectados
			atualizarPontos(dupla);
			
		}
		
		
		
		// reinicia as cartas da jogada em todos os clientes conectados
		for(int i=0; i<4; i++)
			players[i].resetCartasJogadas();
		
		//contador de rodadas é incrementado
		if(cont_rodada == 2)
			cont_rodada = 0;
		else
			cont_rodada++;
		
		//A variavel cangado deve ser setada para false
		cangado = false;
		
		//Ao final da rodada é necessário reinicializar o baralho!
		this.baralho.resetBaralho();
		truco = false;				//a variavel truco é retornada a 1 ponto
		valorRodada = 1;			//o valor da rodada volta ao valor inicial
	}
	
	public void atualizarPontos(int dup) throws RemoteException{
		for(int i = 0; i < 4; i++){
			this.players[i].setPontos(dup, this.placarPartida[dup]);
		}
	}
	
	public static void main(String args[]) {
        try {
          TrucoInterface truco = new TrucoServer();
	      Naming.rebind("//localhost/Truco", truco);
          System.out.println("Servidor no ar!");
        }catch (Exception e) {
	      System.out.println("Erro: " + e.getMessage());
	      e.printStackTrace();
        }
    }
	
}
